-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add support for wrapping the devtools APIs #57
Conversation
Hi @Rob--W |
src/browser-polyfill.js
Outdated
if ("maxResolvedArgs" in metadata) { | ||
// Some chrome APIs provides to their callback more arguments than expected. | ||
callbackArgs = callbackArgs.slice(0, metadata.maxResolvedArgs); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to be so generic. Since the problem only occurs when with 1 vs not-one parameter, I suggest to throw away "maxResolvedArgs" and use "singleCallbackParameter": true
or something similar. This could also be used to fix API definitions for callbacks that don't expect any parameter. Based on the code below, it is likely that callbacks without parameters resolve to an empty array.
test/test-async-functions.js
Outdated
panels: { | ||
create: sinon.spy((title, iconPath, panelURL, cb) => { | ||
Promise.resolve().then(() => { | ||
cb({isFakePanel: true}, "unwanted parameter"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave a comment what happens with "unwanted parameter"
, and why.
@@ -75,28 +105,28 @@ describe("browser-polyfill", () => { | |||
}); | |||
|
|||
it("deletes proxy getter/setter that are not wrapped", () => { | |||
const fakeChrome = {}; | |||
const fakeChrome = {runtime: {}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why these changes? Please explain the changes in the commit description.
I have filed an upstream bug for Chrome's misbehaving devtools.panels.create API: https://bugs.chromium.org/p/chromium/issues/detail?id=768159 |
97695a3
to
676966d
Compare
@Rob--W Thanks a lot for the review comments (and for filing the issue on Chromium issue tracker), I've applied the following changes based on you review comments:
|
676966d
to
d343d0c
Compare
…to wrapped APIs. To be able to wrap the devtools API namespaces, this patch applies the following changes: - fix: Prevent Proxy violation exception on the read-only/non-configurable devtools property by using an empty object with the `chrome` API object as its prototype as the root Proxy target (the Proxy instances returned for the `chrome` API object) and add a related test case. - fix: Added support for a new `singleCallbackArg` metadata property, which prevents devtools.panels.create to resolve an array of parameters (See the related Chromium issue at https://bugs.chromium.org/p/chromium/issues/detail?id=768159) and add the related test cases. - test: Changes to the test case related to proxy getter/setter behavior on non wrapped properties: in the "deletes proxy getter/setter that are not wrapped" test case from the "test-proxied-properties.js" test file, we ensure that when a getter/setter is called for a "non-wrapped" property, the getter/setter is going to affect the original target object, unfortunately this in not true anymore for the root object (the `chrome` API object) because we are using an empty object (which has the `chrome` API object as its prototype and it is not exposed outside of the polyfill sources) as the target of the Proxy instance related to it, this change to the target of the Proxy has been needed to prevent the TypeError exception raised by the Proxy instance when we try to access the "devtools" property (which is non-configurable and read-only on the `chrome` API object).
d343d0c
to
6dc0c38
Compare
Thank you so much for this! Excited to give it a go. Will report back in a new issue if I run into any problems. |
Do you know when this will get released? I'm not seeing it in 0.1.2. Thanks! |
@marcysutton I'm going to release a new version today (I'd like to include also #59, but if we are not able to land it today yet, I will release a new version for this change and I will include #59 in the next one). |
Originally, the polyfill created a Proxy with the original API object as the target. This was changed to `Object.create(chrome)` because not doing so would prevent the `browser.devtools` API from working because the devtools API object is assigned as a read-only & non-configurable property (mozilla#57). However, that action itself caused a new bug: Whenever an API object is dereferenced via the `browser` namespace, the original API is no longer available in the `chrome` namespace, and trying to access the API through `chrome` returns `undefined` plus the "Previous API instantiation failed" warning (mozilla#58). This is because Chrome lazily initializes fields in the `chrome` API, but on the object from which the property is accessed, while the polyfill accessed the property through an object with the prototype set to `chrome` instead of directly via chrome. To fix that, `Object.create(chrome)` was replaced with `Object.assign({}, chrome)`. This fixes both of the previous issues because 1) It is still a new object. 2) All lazily initialized fields are explicitly initialized. This fix created a new issue: In Chrome some APIs cannot be used even though they are visible in the API (e.g. `chrome.clipboard`), so calling `Object.assign({}, chrome)` causes an error to be printed to the console (mozilla#70). To solve this, I use `Object.create(chrome)` again as a proxy target, but dereference the API via the original target (`chrome`) to not regress on mozilla#58. Besides fixing the bug, this also reduces the performance impact of the API because all API fields are lazily initialized again, instead of upon start-up. This fixes mozilla#70.
Originally, the polyfill created a Proxy with the original API object as the target. This was changed to `Object.create(chrome)` because not doing so would prevent the `browser.devtools` API from working because the devtools API object is assigned as a read-only & non-configurable property (mozilla#57). However, that action itself caused a new bug: Whenever an API object is dereferenced via the `browser` namespace, the original API is no longer available in the `chrome` namespace, and trying to access the API through `chrome` returns `undefined` plus the "Previous API instantiation failed" warning (mozilla#58). This is because Chrome lazily initializes fields in the `chrome` API, but on the object from which the property is accessed, while the polyfill accessed the property through an object with the prototype set to `chrome` instead of directly via chrome. To fix that, `Object.create(chrome)` was replaced with `Object.assign({}, chrome)`. This fixes both of the previous issues because 1) It is still a new object. 2) All lazily initialized fields are explicitly initialized. This fix created a new issue: In Chrome some APIs cannot be used even though they are visible in the API (e.g. `chrome.clipboard`), so calling `Object.assign({}, chrome)` causes an error to be printed to the console (mozilla#70). To solve this, I use `Object.create(chrome)` again as a proxy target, but dereference the API via the original target (`chrome`) to not regress on mozilla#58. Besides fixing the bug, this also reduces the performance impact of the API because all API fields are lazily initialized again, instead of upon start-up. This fixes mozilla#70.
Originally, the polyfill created a Proxy with the original API object as the target. This was changed to `Object.create(chrome)` because not doing so would prevent the `browser.devtools` API from working because the devtools API object is assigned as a read-only & non-configurable property (#57). However, that action itself caused a new bug: Whenever an API object is dereferenced via the `browser` namespace, the original API is no longer available in the `chrome` namespace, and trying to access the API through `chrome` returns `undefined` plus the "Previous API instantiation failed" warning (#58). This is because Chrome lazily initializes fields in the `chrome` API, but on the object from which the property is accessed, while the polyfill accessed the property through an object with the prototype set to `chrome` instead of directly via chrome. To fix that, `Object.create(chrome)` was replaced with `Object.assign({}, chrome)`. This fixes both of the previous issues because 1) It is still a new object. 2) All lazily initialized fields are explicitly initialized. This fix created a new issue: In Chrome some APIs cannot be used even though they are visible in the API (e.g. `chrome.clipboard`), so calling `Object.assign({}, chrome)` causes an error to be printed to the console (#70). To solve this, I use `Object.create(chrome)` again as a proxy target, but dereference the API via the original target (`chrome`) to not regress on #58. Besides fixing the bug, this also reduces the performance impact of the API because all API fields are lazily initialized again, instead of upon start-up. This fixes #70.
This PR is applying the following changes to fix #56:
TypeError
exception when the getter on the proxy is related to a non-configurable read-only property of the original Proxy target (fixed by using an empty object which has the original target as its prototype as the "root" target of the Proxy instance created by the polyfill)Proxy TypeError
fix)Proxy TypeError
fix and the newmaxResolvedArgs
property in thedevtools.panels.create
metadata).